#include <stdio.h>
#include <stdlib.h>

// 函数声明：用于按特定规则整理书籍到书架
// bookStack：当前待整理的书堆数组
// stackSize：当前书堆中的书籍数量
// shelf：存放整理后书籍的书架数组
// shelfPos：当前要放置书籍的书架位置索引
void arrangeBooks(int* bookStack, int stackSize, int* shelf, int shelfPos);

int main() {
    int n;
    scanf("%d", &n);  // 读取书籍总数n

    // 初始化书堆：动态分配n个int大小的内存
    int* bookStack = (int*)malloc(n * sizeof(int));
    // 书籍编号从1到n依次放入书堆（初始顺序为1,2,3,...,n）
    for (int i = 0; i < n; i++) {
        bookStack[i] = i + 1;
    }

    // 初始化书架：动态分配n个int大小的内存，用于存放整理后的书籍
    int* shelf = (int*)malloc(n * sizeof(int));

    // 调用函数开始整理书籍，初始书堆为bookStack，大小为n，从书架0号位置开始放
    arrangeBooks(bookStack, n, shelf, 0);

    // 输出书架上整理好的书籍顺序
    for (int i = 0; i < n; i++) {
        printf("%d ", shelf[i]);
    }
    printf("\n");

    // 释放动态分配的内存，避免内存泄漏
    free(bookStack);
    free(shelf);

    return 0;
}

// 递归函数：按特定规则将书堆中的书籍依次放到书架上
void arrangeBooks(int* bookStack, int stackSize, int* shelf, int shelfPos) {
    // 递归终止条件：如果书堆中没有书了，直接返回
    if (stackSize <= 0) {
        return;
    }

    // 1. 将当前书堆最上面的一本书（数组第一个元素）放到书架的当前位置
    shelf[shelfPos] = bookStack[0];

    // 2. 如果书堆中只剩一本书，放好后无需后续处理，直接返回
    if (stackSize == 1) {
        return;
    }

    // 3. 处理剩下的书籍：将除了已放书架的书之外的剩余书籍重新排列
    int remainingSize = stackSize - 1;  // 剩余书籍数量（总数量减1）
    int half = remainingSize / 2;       // 将剩余书籍分为两半（前半部分大小）

    // 4. 交换剩余书籍的前半部分和后半部分，创建新的书堆
    // 动态分配存放新书堆的内存
    int* newStack = (int*)malloc(remainingSize * sizeof(int));

    // 先复制后半部分到新书堆的前半部分
    // 后半部分起始位置：原书堆索引1+half（跳过已放的第一本书和前半部分）
    for (int i = 0; i < remainingSize - half; i++) {
        newStack[i] = bookStack[1 + half + i];
    }

    // 再复制前半部分到新书堆的后半部分
    // 前半部分起始位置：原书堆索引1（跳过已放的第一本书）
    for (int i = 0; i < half; i++) {
        newStack[remainingSize - half + i] = bookStack[1 + i];
    }

    // 5. 递归处理新的书堆：将新书堆、剩余数量、书架下一个位置传入
    arrangeBooks(newStack, remainingSize, shelf, shelfPos + 1);

    // 释放当前函数中动态分配的新书堆内存
    free(newStack);
}